﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;

using HtmlAgilityPack;

namespace Homework2
{
    public static class PageParser
    {
        /// <summary>
        /// Parses the webpage and creates a hypertree from it's HTML code
        /// </summary>
        /// <param name="pageUrl">The URL of the page to parse</param>
        /// <returns>A list of WebNodes to display in the TreeView (should really just be one node, but the item source has to be a list)</returns>
        public static List<WebNode> ParsePage(string pageUrl)
        {
            var nodeList = new List<WebNode>();

            try
            {
                // Use the HtmlAgilityPack to convert the HTML code into an XmlDocument
                var web = new HtmlWeb();
                var memStream = new MemoryStream();
                web.LoadHtmlAsXml(pageUrl, new XmlTextWriter(memStream, null));

                memStream.Position = 0;

                // Load the memory stream from above into an XmlDocument
                var doc = new XmlDocument();
                doc.LoadXml(new StreamReader(memStream).ReadToEnd());

                // Begin reading through the nodes
                // Should grab only HTML node as it is the root of the webpage and anything outside it is likely irrelevant
                var rootHtmlNode = doc.Cast<XmlNode>().First(n => n.NodeType == XmlNodeType.Element && n.Name.Equals("html", StringComparison.InvariantCultureIgnoreCase));
                nodeList.Add(ReadNodes(null, rootHtmlNode));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            return nodeList;
        }

        /// <summary>
        /// Recursively drills down into the XmlNode, building up the nodes and children
        /// </summary>
        /// <param name="parentNode">The parent WebNode associated with the given XmlNode</param>
        /// <param name="xmlNode">The next XmlNode to work with</param>
        /// <returns>A WebNode created from the XmlNode data</returns>
        private static WebNode ReadNodes(WebNode parentNode, XmlNode xmlNode)
        {
            if (xmlNode == null)
            {
                return null;
            }

            // Create the WebNode from the XmlNode
            var newWebNode = new WebNode(parentNode, xmlNode);

            try
            {
                // While this is false, text child nodes are put into the Text property of the
                // parent WebNode.  This is set to true when the first non-text child node is hit
                var hitInnerTag = false;

                // Iterate through the node's children (if there are any...)
                foreach (XmlNode childXmlNode in xmlNode)
                {
                    // If the XmlNode is a text node and we haven't hit a non-text inner child node yet,
                    // add the text from the node to the Text property of the parent WebNode
                    if (childXmlNode.NodeType == XmlNodeType.Text && !hitInnerTag)
                    {
                        newWebNode.Text = newWebNode.Text == null
                                              ? childXmlNode.InnerText
                                              : newWebNode.Text + childXmlNode.InnerText;
                        continue;
                    }

                    hitInnerTag = true; // Hit a non-text inner child node

                    // If the child has child nodes, recurse through them
                    if (childXmlNode.HasChildNodes)
                    {
                        newWebNode.Children.Add(ReadNodes(newWebNode, childXmlNode));
                    }
                    else // ...otherwise make a new WebNode and just add that
                    {
                        var newChildWebNode = new WebNode(newWebNode, childXmlNode);

                        // If it's an inner text node, set the Text property of the new
                        // child WebNode appropriately
                        if (childXmlNode.NodeType == XmlNodeType.Text)
                        {
                            newChildWebNode.Text = childXmlNode.InnerText;
                        }

                        newWebNode.Children.Add(newChildWebNode);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            return newWebNode;
        }
    }
}
